commonlibsse_ng\re\c\Calendar/month.rs
1/// 0-based Month representation.
2///
3/// Internally stores the month as `f32` in the range `0.0..=11.0`, corresponding to:
4/// - `0.0` -> January (Morning Star)
5/// - `1.0` -> February (Sun's Dawn)
6/// - `11.0` -> December (Evening Star)
7///
8/// # Example
9/// ```
10/// use commonlibsse_ng::re::Calendar::MonthIndex;
11///
12/// let month = MonthIndex::new(0.0);
13/// assert_eq!(month.to_clamp_month(), Some(1)); // 1-based month
14///
15/// let month = MonthIndex::new(11.0);
16/// assert_eq!(month.to_clamp_month(), Some(12)); // Evening Star
17///
18/// let month = MonthIndex::new(12.0);
19/// assert_eq!(month.to_clamp_month(), None); // Out of range
20/// ```
21#[derive(Debug, Default, Clone, Copy, PartialEq)]
22#[repr(transparent)]
23pub struct MonthIndex(pub f32);
24
25impl MonthIndex {
26 /// The default month value (`0.0` -> `MorningStar`).
27 pub const DEFAULT: Self = Self(0.0);
28
29 /// Creates a new `MonthIndex` instance with the specified value.
30 ///
31 /// # Example
32 /// ```
33 /// # use commonlibsse_ng::re::Calendar::MonthIndex;
34 /// let month = MonthIndex::new(5.0);
35 /// assert_eq!(month.0, 5.0);
36 /// ```
37 #[inline]
38 pub const fn new(value: f32) -> Self {
39 Self(value)
40 }
41
42 /// Returns the 1-based month (1..=12) if the value is valid, otherwise `None`.
43 ///
44 /// - `0.0` → `1` (January)
45 /// - `11.0` → `12` (December)
46 ///
47 /// Returns `None` if the value is out of the valid range (`0.0..=11.0`).
48 ///
49 /// # Example (Boundary Tests)
50 /// ```
51 /// # use commonlibsse_ng::re::Calendar::MonthIndex;
52 /// assert_eq!(MonthIndex::new(0.0).to_clamp_month(), Some(1)); // Morning Star
53 /// assert_eq!(MonthIndex::new(11.0).to_clamp_month(), Some(12)); // Evening Star
54 /// assert_eq!(MonthIndex::new(12.0).to_clamp_month(), None); // Out of range
55 /// assert_eq!(MonthIndex::new(-1.0).to_clamp_month(), None); // Out of range
56 /// ```
57 #[inline]
58 pub const fn to_clamp_month(self) -> Option<u32> {
59 let n = self.0;
60 match n {
61 0.0..12.0 => Some(n as u32 + 1),
62 _ => None,
63 }
64 }
65
66 /// Converts `MonthIndex` into `MonthInGame` enum if the value is valid.
67 ///
68 /// Returns `None` if the value is out of range (`0.0..=11.0`).
69 ///
70 /// # Example
71 /// ```
72 /// # use commonlibsse_ng::re::Calendar::{MonthIndex, MonthInGame};
73 /// let month = MonthIndex::new(0.0);
74 /// assert_eq!(month.to_enum(), Some(MonthInGame::MorningStar));
75 ///
76 /// let invalid_month = MonthIndex::new(12.0);
77 /// assert_eq!(invalid_month.to_enum(), None);
78 /// ```
79 #[inline]
80 pub const fn to_enum(self) -> Option<MonthInGame> {
81 MonthInGame::from_u32(self.0 as u32)
82 }
83}
84
85/// Represents the months of the year.
86///
87/// The internal values correspond to 0-based month indexing:
88/// - `0` → January (`MorningStar`)
89/// - `1` → February (`SunsDawn`)
90/// - `11` → December (`EveningStar`)
91///
92/// # Example
93/// ```
94/// # use commonlibsse_ng::re::Calendar::MonthInGame;
95/// let month = MonthInGame::FirstSeed;
96/// assert_eq!(month.as_str(), "First Seed");
97/// ```
98#[repr(u32)]
99#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub enum MonthInGame {
101 MorningStar = 0,
102 SunsDawn = 1,
103 FirstSeed = 2,
104 RainsHand = 3,
105 SecondSeed = 4,
106 Midyear = 5,
107 SunsHeight = 6,
108 #[default]
109 LastSeed = 7,
110 Hearthfire = 8,
111 Frostfall = 9,
112 SunsDusk = 10,
113 EveningStar = 11,
114 // Total, // unused
115}
116
117impl MonthInGame {
118 /// Returns the string representation of the month name.
119 ///
120 /// # Example
121 /// ```
122 /// # use commonlibsse_ng::re::Calendar::MonthInGame;
123 /// let month = MonthInGame::Midyear;
124 /// assert_eq!(month.as_str(), "Midyear");
125 /// ```
126 #[inline]
127 pub const fn as_str(&self) -> &'static str {
128 match *self {
129 Self::MorningStar => "Morning Star",
130 Self::SunsDawn => "Sun's Dawn",
131 Self::FirstSeed => "First Seed",
132 Self::RainsHand => "Rain's Hand",
133 Self::SecondSeed => "Second Seed",
134 Self::Midyear => "Midyear",
135 Self::SunsHeight => "Sun's Height",
136 Self::LastSeed => "Last Seed",
137 Self::Hearthfire => "Hearthfire",
138 Self::Frostfall => "Frostfall",
139 Self::SunsDusk => "Sun's Dusk",
140 Self::EveningStar => "Evening Star",
141 }
142 }
143
144 /// Converts a `u32` value into the corresponding `MonthInGame` enum.
145 ///
146 /// Returns `None` if the value is out of range (`0..=11`).
147 ///
148 /// # Example
149 /// ```
150 /// # use commonlibsse_ng::re::Calendar::MonthInGame;
151 ///
152 /// assert_eq!(MonthInGame::from_u32(0), Some(MonthInGame::MorningStar)); // January
153 /// assert_eq!(MonthInGame::from_u32(11), Some(MonthInGame::EveningStar)); // December
154 /// assert_eq!(MonthInGame::from_u32(12), None); // Out of range
155 /// assert_eq!(MonthInGame::from_u32(100), None); // Out of range
156 /// ```
157 #[inline]
158 pub const fn from_u32(month: u32) -> Option<Self> {
159 Some(match month {
160 0 => Self::MorningStar,
161 1 => Self::SunsDawn,
162 2 => Self::FirstSeed,
163 3 => Self::RainsHand,
164 4 => Self::SecondSeed,
165 5 => Self::Midyear,
166 6 => Self::SunsHeight,
167 7 => Self::LastSeed,
168 8 => Self::Hearthfire,
169 9 => Self::Frostfall,
170 10 => Self::SunsDusk,
171 11 => Self::EveningStar,
172 _ => return None,
173 })
174 }
175}
176
177impl From<MonthInGame> for MonthIndex {
178 #[inline]
179 fn from(month: MonthInGame) -> Self {
180 Self(month as u32 as f32)
181 }
182}
183
184impl TryFrom<MonthIndex> for MonthInGame {
185 type Error = &'static str;
186
187 #[inline]
188 fn try_from(index: MonthIndex) -> Result<Self, Self::Error> {
189 let u32_index = index.0 as u32;
190 Self::from_u32(u32_index).ok_or("Invalid month index")
191 }
192}
193
194impl core::fmt::Display for MonthInGame {
195 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196 write!(f, "{}", self.as_str())
197 }
198}